/*
 * Copyright ©2015-2021 Jaemon. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gitee.jaemon.spring.adorner.core;

import io.gitee.jaemon.spring.adorner.exception.AdornerException;
import io.gitee.jaemon.spring.adorner.support.AdornerBanner;
import io.gitee.jaemon.spring.adorner.support.DefaultIndexConvert;
import io.gitee.jaemon.spring.adorner.support.MarkdownApiShow;
import io.gitee.jaemon.spring.adorner.type.CommonParameter;
import io.gitee.jaemon.spring.adorner.type.DocScanner;
import io.gitee.jaemon.spring.adorner.type.LogLevel;
import io.gitee.jaemon.spring.adorner.util.StringUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * adorner上下文对象
 *
 * @author Jaemon
 * @since 1.0
 */
public class AdornerApplicationContext {
    public static final String DEFAULT_URL = "http://localhost:8080/adorner";

    /** 文件存储路径 */
    private String filePath;
    /** 扫描包路径 */
    private String[] basePackages;
    /** 统一地址前缀, <url>http://localhost:8080/adorner</url> */
    private String url;
    /** 是否按照controller分组展示 */
    private boolean group;
    /** 简化展示, 为true时展示接口配置的所有请求地址和请求方法 */
    private Boolean simplify;
    /** 文档注释扫描器 */
    private DocScanner docScanner;
    /** 日志文件路径, 设值则默认存储日志到文件, EG： /home/jaemon/adorner/ */
    private String logpath;
    /** 是否存储日志文件 */
    private boolean logger;
    /**
     * 只对包含的类进行处理
     */
    private List<Class<?>> includeClasses;
    /**
     * 需要排除的类
     */
    private List<Class<?>> excludeClasses;
    /**
     * 请求参数公共部分
     */
    private CommonParameter commonParameter;
    /**
     * Api接口参数展示, 默认 {@link MarkdownApiShow#JSON}
     */
    private ApiParamShow apiParamShow;
    /**
     * 索引转换器, 默认 {@link DefaultIndexConvert}
     */
    private IndexConvert indexConvert;
    /**
     * 系统日志级别, 默认{@link LogLevel#INFO}
     */
    private LogLevel logLevel;
    /**
     * 标语信息, 默认{@link io.gitee.jaemon.spring.adorner.support.AdornerBanner}
     */
    private Banner banner;

    AdornerApplicationContext(AdornerApplicationContext.Builder builder) {
        this.filePath = builder.filePath;
        this.basePackages = builder.basePackages;
        this.url = StringUtils.isEmpty(builder.url) ? DEFAULT_URL : builder.url;
        this.group = builder.group;
        this.simplify = builder.simplify == null ? true : builder.simplify;

        List<String> baseScanPackages = Arrays.stream(this.basePackages).collect(Collectors.toList());
        DocScanner docScanner = builder.docScanner;
        if (docScanner == null) {
            docScanner = DocScanner.of(baseScanPackages);
        } else {
            baseScanPackages.addAll(docScanner.getPackages());
        }
        this.docScanner = DocScanner.of(
                baseScanPackages.stream().distinct().collect(Collectors.toList()),
                docScanner.getClassPath()
        );

        String logPath = builder.logpath;
        if (StringUtils.isNotEmpty(logPath)) {
            if (!logPath.endsWith("/")) {
                logPath = logPath + File.separator;
            }

            File logFilePath = new File(logPath);
            if (!logFilePath.exists()) {
                logFilePath.mkdirs();
            }
            if (logFilePath.isFile()) {
                logFilePath.delete();
                throw new AdornerException("%s is a file.", builder.logpath);
            }
            this.logger = true;
        }
        this.logpath = logPath;

        this.includeClasses = builder.includeClasses == null ?
                new ArrayList<>() : builder.includeClasses;
        this.excludeClasses = builder.excludeClasses == null ?
                new ArrayList<>() : builder.excludeClasses;
        this.commonParameter = builder.commonParameter;
        this.apiParamShow = builder.apiParamShow == null ?
                MarkdownApiShow.JSON : builder.apiParamShow;
        this.indexConvert = builder.indexConvert == null ?
                new DefaultIndexConvert() : builder.indexConvert;
        this.logLevel = builder.logLevel == null ?
                LogLevel.INFO : builder.logLevel;
        this.banner = builder.banner == null ?
                new AdornerBanner() : builder.banner;
    }

    public static AdornerApplicationContext.Builder builder() {
        return new AdornerApplicationContext.Builder();
    }

    /**
     * 文件存储路径
     *
     * @return
     *      filePath
     */
    public String filePath() {
        return filePath;
    }

    /**
     * 扫描包路径
     *
     * @return
     *      basePackages
     */
    public String[] basePackages() {
        return this.basePackages;
    }

    /**
     * 统一地址前缀
     *
     * @return
     *      url
     */
    public String url() {
        return this.url;
    }

    /**
     * 是否按照controller分组展示
     *
     * @return
     *      true | false
     */
    public boolean group() {
        return this.group;
    }

    /**
     * 简化展示
     *
     * @return
     *      true | false
     */
    public boolean simplify() {
        return this.simplify;
    }

    /**
     * 文档注释扫描器
     *
     * @return
     *      docScanner
     */
    public DocScanner docScanner() {
        return this.docScanner;
    }

    /**
     * 日志文件路径, 设值则默认存储日志到文件
     *
     * @return
     *      logpath
     */
    public String logpath() {
        return this.logpath;
    }

    /**
     * 是否存储日志
     *
     * @return
     *      true | false
     */
    public boolean isLogger() {
        return this.logger;
    }

    /**
     * 只对包含的类进行处理
     *
     * @return
     *      包含的类集合
     */
    public List<Class<?>> includeClasses() {
        return this.includeClasses;
    }

    /**
     * 需要排除的类
     *
     * @return
     *      需要排除的类集合
     */
    public List<Class<?>> excludeClasses() {
        return this.excludeClasses;
    }

    /**
     * 请求参数公共部分
     *
     * @return
     *      {@link CommonParameter}
     */
    public CommonParameter commonParameter() {
        return this.commonParameter;
    }

    /**
     * Api接口参数展示, 默认 {@link MarkdownApiShow#JSON}
     *
     * @return
     *      {@link ApiParamShow}
     */
    public ApiParamShow apiParamShow() {
        return this.apiParamShow;
    }

    /**
     * 索引转换器, 默认 {@link DefaultIndexConvert}
     *
     * @return
     *      {@link IndexConvert}
     */
    public IndexConvert indexConvert() {
        return this.indexConvert;
    }

    /**
     * 系统日志级别, 默认{@link LogLevel#INFO}
     *
     * @return
     *      {@link LogLevel}
     */
    public LogLevel logLevel() {
        return this.logLevel;
    }

    /**
     * 标语信息, 默认{@link io.gitee.jaemon.spring.adorner.support.AdornerBanner}
     *
     * @return
     *      {@link Banner}
     */
    public Banner banner() {
        return this.banner;
    }

    public String show() {
        return "AdornerApplicationContext(filePath=" + this.filePath + ", basePackages=" + Arrays.deepToString(this.basePackages) + ", url=" + this.url + ", group=" + this.group + ", simplify=" + this.simplify + ", docScanner=" + this.docScanner + ", logpath=" + this.logpath + ", logger=" + this.isLogger() + ", includeClasses=" + this.includeClasses + ", excludeClasses=" + this.excludeClasses + ", commonParameter=" + this.commonParameter + ", apiParamShow=" + this.apiParamShow + ", indexConvert=" + this.indexConvert + ", logLevel=" + this.logLevel + ", banner=" + this.banner + ")";
    }


    public static class Builder {
        /** 文件存储路径 */
        private String filePath;
        /** 扫描包路径 */
        private String[] basePackages;
        /** 统一地址前缀, <url>http://localhost:8080/adorner</url> */
        private String url;
        /** 是否按照controller分组展示 */
        private boolean group;
        /** 简化展示, 为true时展示接口配置的所有请求地址和请求方法 */
        private Boolean simplify;
        /** 文档注释扫描器 */
        private DocScanner docScanner;
        /** 日志文件路径, 设值则默认存储日志到文件 */
        private String logpath;
        /**
         * 需要排除的类
         */
        private List<Class<?>> excludeClasses;
        /**
         * 只对包含的类进行处理
         */
        private List<Class<?>> includeClasses;
        /**
         * 请求参数公共部分
         */
        private CommonParameter commonParameter;
        /**
         * Api展示类型, 默认 {@link MarkdownApiShow#JSON}
         */
        private ApiParamShow apiParamShow;
        /**
         * 索引转换器, 默认 {@link DefaultIndexConvert}
         */
        private IndexConvert indexConvert;
        /**
         * 系统日志级别, 默认{@link LogLevel#INFO}
         */
        private LogLevel logLevel;
        /**
         * 标语信息, 默认{@link io.gitee.jaemon.spring.adorner.support.AdornerBanner}
         */
        private Banner banner;

        Builder() {
        }

        /**
         * 设置地址
         *
         * @param url
         *      项目地址
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder url(final String url) {
            this.url = url;
            return this;
        }

        /**
         * 是否需要按类进行分组 {@link io.gitee.jaemon.spring.adorner.annotation.GroupNote}
         *
         * @param group
         *      true | false
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder group(final boolean group) {
            this.group = group;
            return this;
        }

        /**
         * 设置是否简化输出
         *
         * @param simplify
         *      true(只展示一个请求地址和请求方法) | false
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder simplify(final boolean simplify) {
            this.simplify = simplify;
            return this;
        }

        /**
         * 设置文档注释扫描器
         *
         * @param docScanner
         *     文档注释扫描器
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder docScanner(final DocScanner docScanner) {
            this.docScanner = docScanner;
            return this;
        }

        /**
         * 日志文件路径, 设值则默认存储日志到文件
         *
         * @param logpath
         *     日志文件存储路径
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder logpath(final String logpath) {
            this.logpath = logpath;
            return this;
        }

        /**
         * 只对包含的类进行处理, 优先级高于excludeClasses
         *
         * @param includeClasses
         *     包含的类
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder includeClasses(final List<Class<?>> includeClasses) {
            this.includeClasses = includeClasses;
            return this;
        }

        /**
         * 需要排除的接口
         *
         * @param excludeClasses
         *     排除的类集合
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder excludeClasses(final List<Class<?>> excludeClasses) {
            this.excludeClasses = excludeClasses;
            return this;
        }

        /**
         * 请求参数公共部分
         *
         * @param commonParameter
         *      请求参数公共部分{@link CommonParameter}
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder commonParameter(final CommonParameter commonParameter) {
            this.commonParameter = commonParameter;
            return this;
        }

        /**
         * Api接口参数展示, 默认 {@link MarkdownApiShow#JSON}
         *
         * @param apiParamShow
         *      Api接口参数展示{@link ApiParamShow}
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder apiParamShow(final ApiParamShow apiParamShow) {
            this.apiParamShow = apiParamShow;
            return this;
        }

        /**
         * 索引转换器, 默认 {@link DefaultIndexConvert}
         *
         * @param indexConvert
         *      索引转换器
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder indexConvert(final IndexConvert indexConvert) {
            this.indexConvert = indexConvert;
            return this;
        }

        /**
         * 系统日志级别, 默认{@link LogLevel#INFO}
         *
         * @param logLevel
         *      日志级别
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder logLevel(final LogLevel logLevel) {
            this.logLevel = logLevel;
            return this;
        }

        /**
         * 标语信息, 默认{@link io.gitee.jaemon.spring.adorner.support.AdornerBanner}
         *
         * @param banner
         *      标语对象
         * @return
         *      this
         */
        public AdornerApplicationContext.Builder banner(final Banner banner) {
            this.banner = banner;
            return this;
        }


        /**
         * 构造上下文对象
         *
         * @param filePath
         *      文件路径
         * @param basePackage
         *      扫描接口包
         * @return
         *      adornerApplicationContext {@link AdornerApplicationContext}
         */
        public AdornerApplicationContext build(final String filePath, final String basePackage) {
            this.filePath = filePath;
            this.basePackages = new String[]{basePackage};
            return new AdornerApplicationContext(this);
        }

        /**
         * 构造上下文对象
         *
         * @param filePath
         *      文件路径
         * @param basePackages
         *      扫描接口包数组
         * @return
         *      adornerApplicationContext {@link AdornerApplicationContext}
         */
        public AdornerApplicationContext build(final String filePath, final String[] basePackages) {
            this.filePath = filePath;
            this.basePackages = basePackages;
            return new AdornerApplicationContext(this);
        }
    }
}